﻿using System;
using GeoAPI.Geometries;

namespace YJKGridTopologyCal.LinearReferencing
{
	public class LengthLocationMap
	{
		public static LinearLocation GetLocation(IGeometry linearGeom, double length)
		{
			return new LengthLocationMap(linearGeom).GetLocation(length);
		}

		public static LinearLocation GetLocation(IGeometry linearGeom, double length, bool resolveLower)
		{
			return new LengthLocationMap(linearGeom).GetLocation(length, resolveLower);
		}

		public static double GetLength(IGeometry linearGeom, LinearLocation loc)
		{
			return new LengthLocationMap(linearGeom).GetLength(loc);
		}

		public LengthLocationMap(IGeometry linearGeom)
		{
			this._linearGeom = linearGeom;
		}

		public LinearLocation GetLocation(double length)
		{
			return this.GetLocation(length, true);
		}

		public LinearLocation GetLocation(double length, bool resolveLower)
		{
			double length2 = length;
			if (length < 0.0)
			{
				length2 = this._linearGeom.Length + length;
			}
			LinearLocation locationForward = this.GetLocationForward(length2);
			if (resolveLower)
			{
				return locationForward;
			}
			return this.ResolveHigher(locationForward);
		}

		private LinearLocation GetLocationForward(double length)
		{
			if (length <= 0.0)
			{
				return new LinearLocation();
			}
			double num = 0.0;
			LinearIterator linearIterator = new LinearIterator(this._linearGeom);
			while (linearIterator.HasNext())
			{
				if (linearIterator.IsEndOfLine)
				{
					if (num == length)
					{
						int componentIndex = linearIterator.ComponentIndex;
						int vertexIndex = linearIterator.VertexIndex;
						return new LinearLocation(componentIndex, vertexIndex, 0.0);
					}
				}
				else
				{
					Coordinate segmentStart = linearIterator.SegmentStart;
					double num2 = linearIterator.SegmentEnd.Distance(segmentStart);
					if (num + num2 > length)
					{
						double segmentFraction = (length - num) / num2;
						int componentIndex2 = linearIterator.ComponentIndex;
						int vertexIndex2 = linearIterator.VertexIndex;
						return new LinearLocation(componentIndex2, vertexIndex2, segmentFraction);
					}
					num += num2;
				}
				linearIterator.Next();
			}
			return LinearLocation.GetEndLocation(this._linearGeom);
		}

		private LinearLocation ResolveHigher(LinearLocation loc)
		{
			if (!loc.IsEndpoint(this._linearGeom))
			{
				return loc;
			}
			int num = loc.ComponentIndex;
			if (num >= this._linearGeom.NumGeometries - 1)
			{
				return loc;
			}
			do
			{
				num++;
			}
			while (num < this._linearGeom.NumGeometries - 1 && this._linearGeom.GetGeometryN(num).Length == 0.0);
			return new LinearLocation(num, 0, 0.0);
		}

		public double GetLength(LinearLocation loc)
		{
			double num = 0.0;
			LinearIterator linearIterator = new LinearIterator(this._linearGeom);
			while (linearIterator.HasNext())
			{
				if (!linearIterator.IsEndOfLine)
				{
					Coordinate segmentStart = linearIterator.SegmentStart;
					double num2 = linearIterator.SegmentEnd.Distance(segmentStart);
					if (loc.ComponentIndex == linearIterator.ComponentIndex && loc.SegmentIndex == linearIterator.VertexIndex)
					{
						return num + num2 * loc.SegmentFraction;
					}
					num += num2;
				}
				linearIterator.Next();
			}
			return num;
		}

		private readonly IGeometry _linearGeom;
	}
}
